 //
 //  CalculatorBrain.swift
 //  Calculator
 //
 //  Created by 朱劲松 on 16/3/8.
 //  Copyright © 2016年 ezjs. All rights reserved.
 //
 
 import Foundation
 
 class CalculatorBrain {
    
    private enum Op: CustomStringConvertible {
        case Operand(Double) // operand:操作数
        case UnaryOperation(String, Double -> Double)
        case BinaryOperation(String, (Double, Double) -> Double)
        
        var description: String {
            get {
                switch self{
                case .Operand(let operand):
                    return "\(operand)"
                case .UnaryOperation(let symble, _):
                    return symble
                case .BinaryOperation(let symble, _):
                    return symble
                }

            }
        }
    }
    
    private var opStack = [Op]()
    
    //    var knownOps = Dictionary<String, Op>()
    private var knownOps = [String: Op]()
    
    init(){
        func learnOp(op: Op){
            knownOps[op.description] = op
        }
        
        //        knownOps[""] = Op.BinaryOperation("", { $0 * $1} )
        //        knownOps[""] = Op.BinaryOperation("") { $0 * $1 }
//        knownOps["×"] = Op.BinaryOperation("×", * )
        learnOp(Op.BinaryOperation("×", *))
        knownOps["÷"] = Op.BinaryOperation("÷") { $1 / $0 }
        knownOps["+"] = Op.BinaryOperation("+", + )
        knownOps["−"] = Op.BinaryOperation("−") { $1 - $0  }
        
    }
    
    private func evalaute(ops: [Op]) -> (result: Double?, remainingOps: [Op] ) {
        if !ops.isEmpty {
            var remainingOps = ops
            let op = remainingOps.removeLast()
            
            switch op {
            case .Operand(let operand):
                return (operand, remainingOps)
            case .UnaryOperation(_, let operation):
                let operandEvaluation = evalaute(remainingOps)
                if let operand  = operandEvaluation.result {
                    return (operation(operand), operandEvaluation.remainingOps)
                }
            case .BinaryOperation(_, let operation):
                let op1Evaluation = evalaute(remainingOps)
                if let op1Operand = op1Evaluation.result {
                    let op2Evaluation = evalaute(op1Evaluation.remainingOps)
                    if let op2Operand = op2Evaluation.result {
                        return (operation(op1Operand, op2Operand), op2Evaluation.remainingOps)
                    }
                }
            }
        }
        print("----error with input")
        return (nil, ops)
    }
    
    func evalaute() -> Double? {
        let (result, remainder) = evalaute(opStack)
        print("\(opStack) = \(result) with \(remainder) left over")
        return result
        
    }
    
    func pushOprand(oprand: Double) -> Double? {
        opStack.append(Op.Operand(oprand))
        return evalaute()
    }
    
    func performOperation(symble: String) -> Double? {
        if let operation = knownOps[symble] {
            opStack.append(operation)
        }
        return evalaute()
    }
 }